/* do not edit automatically generated by mc from M2Dependent.  */
/* M2Dependent.mod implements the run time module dependencies.

Copyright (C) 2022-2023 Free Software Foundation, Inc.
Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.

This file is part of GNU Modula-2.

GNU Modula-2 is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.

GNU Modula-2 is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

Under Section 7 of GPL version 3, you are granted additional
permissions described in the GCC Runtime Library Exception, version
3.1, as published by the Free Software Foundation.

You should have received a copy of the GNU General Public License and
a copy of the GCC Runtime Library Exception along with this program;
see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
<http://www.gnu.org/licenses/>.  */

#include "config.h"
#include "system.h"
#include <stdbool.h>
#   if !defined (PROC_D)
#      define PROC_D
       typedef void (*PROC_t) (void);
       typedef struct { PROC_t proc; } PROC;
#   endif

#   if !defined (TRUE)
#      define TRUE (1==1)
#   endif

#   if !defined (FALSE)
#      define FALSE (1==0)
#   endif

#   include "GStorage.h"
#if defined(__cplusplus)
#   undef NULL
#   define NULL 0
#endif
#define _M2Dependent_H
#define _M2Dependent_C

#   include "Glibc.h"
#   include "GM2LINK.h"
#   include "GASCII.h"
#   include "GSYSTEM.h"
#   include "GStorage.h"
#   include "GStrLib.h"
#   include "GM2RTS.h"

typedef struct M2Dependent_ArgCVEnvP_p M2Dependent_ArgCVEnvP;

typedef struct M2Dependent_DependencyList_r M2Dependent_DependencyList;

typedef struct M2Dependent__T2_r M2Dependent__T2;

typedef M2Dependent__T2 *M2Dependent_ModuleChain;

typedef struct M2Dependent__T3_a M2Dependent__T3;

typedef enum {M2Dependent_unregistered, M2Dependent_unordered, M2Dependent_started, M2Dependent_ordered, M2Dependent_user} M2Dependent_DependencyState;

typedef void (*M2Dependent_ArgCVEnvP_t) (int, void *, void *);
struct M2Dependent_ArgCVEnvP_p { M2Dependent_ArgCVEnvP_t proc; };

struct M2Dependent_DependencyList_r {
                                      PROC proc;
                                      bool forced;
                                      bool forc;
                                      bool appl;
                                      M2Dependent_DependencyState state;
                                    };

struct M2Dependent__T3_a { M2Dependent_ModuleChain array[M2Dependent_user-M2Dependent_unregistered+1]; };
struct M2Dependent__T2_r {
                           void *name;
                           void *libname;
                           M2Dependent_ArgCVEnvP init;
                           M2Dependent_ArgCVEnvP fini;
                           M2Dependent_DependencyList dependency;
                           M2Dependent_ModuleChain prev;
                           M2Dependent_ModuleChain next;
                         };

static M2Dependent__T3 Modules;
static bool Initialized;
static bool WarningTrace;
static bool ModuleTrace;
static bool HexTrace;
static bool DependencyTrace;
static bool PreTrace;
static bool PostTrace;
static bool ForceTrace;

/*
   ConstructModules - resolve dependencies and then call each
                      module constructor in turn.
*/

extern "C" void M2Dependent_ConstructModules (void * applicationmodule, void * libname, int argc, void * argv, void * envp);

/*
   DeconstructModules - resolve dependencies and then call each
                        module constructor in turn.
*/

extern "C" void M2Dependent_DeconstructModules (void * applicationmodule, void * libname, int argc, void * argv, void * envp);

/*
   RegisterModule - adds module name to the list of outstanding
                    modules which need to have their dependencies
                    explored to determine initialization order.
*/

extern "C" void M2Dependent_RegisterModule (void * modulename, void * libname, M2Dependent_ArgCVEnvP init, M2Dependent_ArgCVEnvP fini, PROC dependencies);

/*
   RequestDependant - used to specify that modulename is dependant upon
                      module dependantmodule.  It only takes effect
                      if we are not using StaticInitialization.
*/

extern "C" void M2Dependent_RequestDependant (void * modulename, void * libname, void * dependantmodule, void * dependantlibname);

/*
   CreateModule - creates a new module entry and returns the
                  ModuleChain.
*/

static M2Dependent_ModuleChain CreateModule (void * name, void * libname, M2Dependent_ArgCVEnvP init, M2Dependent_ArgCVEnvP fini, PROC dependencies);

/*
   AppendModule - append chain to end of the list.
*/

static void AppendModule (M2Dependent_ModuleChain *head, M2Dependent_ModuleChain chain);

/*
   RemoveModule - remove chain from double linked list head.
*/

static void RemoveModule (M2Dependent_ModuleChain *head, M2Dependent_ModuleChain chain);

/*
   onChain - returns TRUE if mptr is on the Modules[state] list.
*/

static bool onChain (M2Dependent_DependencyState state, M2Dependent_ModuleChain mptr);

/*
   max -
*/

static unsigned int max (unsigned int a, unsigned int b);

/*
   min -
*/

static unsigned int min (unsigned int a, unsigned int b);

/*
   LookupModuleN - lookup module from the state list.
                   The strings lengths are known.
*/

static M2Dependent_ModuleChain LookupModuleN (M2Dependent_DependencyState state, void * name, unsigned int namelen, void * libname, unsigned int libnamelen);

/*
   LookupModule - lookup and return the ModuleChain pointer containing
                  module name from a particular list.
*/

static M2Dependent_ModuleChain LookupModule (M2Dependent_DependencyState state, void * name, void * libname);

/*
   toCString - replace any character sequence 
 into a newline.
*/

static void toCString (char *str, unsigned int _str_high);

/*
   strcmp - return 0 if both strings are equal.
            We cannot use Builtins.def during bootstrap.
*/

static int strcmp (M2LINK_PtrToChar a, M2LINK_PtrToChar b);

/*
   strncmp - return 0 if both strings are equal.
             We cannot use Builtins.def during bootstrap.
*/

static int strncmp (M2LINK_PtrToChar a, M2LINK_PtrToChar b, unsigned int n);

/*
   strlen - returns the length of string.
*/

static int strlen_ (M2LINK_PtrToChar string);

/*
   traceprintf - wrap printf with a boolean flag.
*/

static void traceprintf (bool flag, const char *str_, unsigned int _str_high);

/*
   traceprintf2 - wrap printf with a boolean flag.
*/

static void traceprintf2 (bool flag, const char *str_, unsigned int _str_high, void * arg);

/*
   traceprintf3 - wrap printf with a boolean flag.
*/

static void traceprintf3 (bool flag, const char *str_, unsigned int _str_high, void * arg1, void * arg2);

/*
   moveTo - moves mptr to the new list determined by newstate.
            It updates the mptr state appropriately.
*/

static void moveTo (M2Dependent_DependencyState newstate, M2Dependent_ModuleChain mptr);

/*
   ResolveDependant -
*/

static void ResolveDependant (M2Dependent_ModuleChain mptr, void * currentmodule, void * libname);

/*
   PerformRequestDependant - the current modulename has a dependancy upon
                             dependantmodule.  If dependantmodule is NIL then
                             modulename has no further dependants and it can be
                             resolved.
*/

static void PerformRequestDependant (void * modulename, void * libname, void * dependantmodule, void * dependantlibname);

/*
   ResolveDependencies - resolve dependencies for currentmodule, libname.
*/

static void ResolveDependencies (void * currentmodule, void * libname);

/*
   DisplayModuleInfo - displays all module in the state.
*/

static void DisplayModuleInfo (M2Dependent_DependencyState state, const char *desc_, unsigned int _desc_high);

/*
   DumpModuleData -
*/

static void DumpModuleData (bool flag);

/*
   combine - dest := src + dest.  Places src at the front of list dest.
             Pre condition:  src, dest are lists.
             Post condition : dest := src + dest
                              src := NIL.
*/

static void combine (M2Dependent_DependencyState src, M2Dependent_DependencyState dest);

/*
   tracemodule -
*/

static void tracemodule (bool flag, void * modname, unsigned int modlen, void * libname, unsigned int liblen);

/*
   ForceModule -
*/

static void ForceModule (void * modname, unsigned int modlen, void * libname, unsigned int liblen);

/*
   ForceDependencies - if the user has specified a forced order then we override
                       the dynamic ordering with the preference.
*/

static void ForceDependencies (void);

/*
   CheckApplication - check to see that the application is the last entry in the list.
                      This might happen if the application only imports FOR C modules.
*/

static void CheckApplication (void);

/*
   warning3 - write format arg1 arg2 to stderr.
*/

static void warning3 (const char *format_, unsigned int _format_high, void * arg1, void * arg2);

/*
   equal - return TRUE if C string cstr is equal to str.
*/

static bool equal (void * cstr, const char *str_, unsigned int _str_high);

/*
   SetupDebugFlags - By default assigns ModuleTrace, DependencyTrace,
                     DumpPostInit to FALSE.  It checks the environment
                     GCC_M2LINK_RTFLAG which can contain
                     "all,module,hex,pre,post,dep,force".  all turns them all on.
                     The flag meanings are as follows and flags the are in
                     execution order.

                     module   generate trace info as the modules are registered.
                     hex      dump the modules ctor functions address in hex.
                     pre      generate a list of all modules seen prior to having
                              their dependancies resolved.
                     dep      display a trace as the modules are resolved.
                     post     generate a list of all modules seen after having
                              their dependancies resolved dynamically.
                     force    generate a list of all modules seen after having
                              their dependancies resolved and forced.
*/

static void SetupDebugFlags (void);

/*
   Init - initialize the debug flags and set all lists to NIL.
*/

static void Init (void);

/*
   CheckInitialized - checks to see if this module has been initialized
                      and if it has not it calls Init.  We need this
                      approach as this module is called by module ctors
                      before we reach main.
*/

static void CheckInitialized (void);


/*
   CreateModule - creates a new module entry and returns the
                  ModuleChain.
*/

static M2Dependent_ModuleChain CreateModule (void * name, void * libname, M2Dependent_ArgCVEnvP init, M2Dependent_ArgCVEnvP fini, PROC dependencies)
{
  M2Dependent_ModuleChain mptr;
  void * p0;
  void * p1;

  Storage_ALLOCATE ((void **) &mptr, sizeof (M2Dependent__T2));
  mptr->name = name;
  mptr->libname = libname;
  mptr->init = init;
  mptr->fini = fini;
  mptr->dependency.proc = dependencies;
  mptr->dependency.state = M2Dependent_unregistered;
  mptr->prev = NULL;
  mptr->next = NULL;
  if (HexTrace)
    {
      libc_printf ((const char *) "   (init: %p  fini: %p", 22, init, fini);
      libc_printf ((const char *) "  dep: %p)", 10, dependencies);
    }
  return mptr;
  /* static analysis guarentees a RETURN statement will be used before here.  */
  __builtin_unreachable ();
}


/*
   AppendModule - append chain to end of the list.
*/

static void AppendModule (M2Dependent_ModuleChain *head, M2Dependent_ModuleChain chain)
{
  if ((*head) == NULL)
    {
      (*head) = chain;
      chain->prev = chain;
      chain->next = chain;
    }
  else
    {
      chain->next = (*head);  /* Add Item to the end of list.  */
      chain->prev = (*head)->prev;  /* Add Item to the end of list.  */
      (*head)->prev->next = chain;
      (*head)->prev = chain;
    }
}


/*
   RemoveModule - remove chain from double linked list head.
*/

static void RemoveModule (M2Dependent_ModuleChain *head, M2Dependent_ModuleChain chain)
{
  if ((chain->next == (*head)) && (chain == (*head)))
    {
      (*head) = NULL;
    }
  else
    {
      if ((*head) == chain)
        {
          (*head) = (*head)->next;
        }
      chain->prev->next = chain->next;
      chain->next->prev = chain->prev;
    }
}


/*
   onChain - returns TRUE if mptr is on the Modules[state] list.
*/

static bool onChain (M2Dependent_DependencyState state, M2Dependent_ModuleChain mptr)
{
  M2Dependent_ModuleChain ptr;

  if (Modules.array[state-M2Dependent_unregistered] != NULL)
    {
      ptr = Modules.array[state-M2Dependent_unregistered];
      do {
        if (ptr == mptr)
          {
            return true;
          }
        ptr = ptr->next;
      } while (! (ptr == Modules.array[state-M2Dependent_unregistered]));
    }
  return false;
  /* static analysis guarentees a RETURN statement will be used before here.  */
  __builtin_unreachable ();
}


/*
   max -
*/

static unsigned int max (unsigned int a, unsigned int b)
{
  if (a > b)
    {
      return a;
    }
  else
    {
      return b;
    }
  /* static analysis guarentees a RETURN statement will be used before here.  */
  __builtin_unreachable ();
}


/*
   min -
*/

static unsigned int min (unsigned int a, unsigned int b)
{
  if (a < b)
    {
      return a;
    }
  else
    {
      return b;
    }
  /* static analysis guarentees a RETURN statement will be used before here.  */
  __builtin_unreachable ();
}


/*
   LookupModuleN - lookup module from the state list.
                   The strings lengths are known.
*/

static M2Dependent_ModuleChain LookupModuleN (M2Dependent_DependencyState state, void * name, unsigned int namelen, void * libname, unsigned int libnamelen)
{
  M2Dependent_ModuleChain ptr;

  if (Modules.array[state-M2Dependent_unregistered] != NULL)
    {
      ptr = Modules.array[state-M2Dependent_unregistered];
      do {
        if (((strncmp (reinterpret_cast<M2LINK_PtrToChar> (ptr->name), reinterpret_cast<M2LINK_PtrToChar> (name), max (namelen, static_cast<unsigned int> (strlen_ (reinterpret_cast<M2LINK_PtrToChar> (ptr->name)))))) == 0) && ((strncmp (reinterpret_cast<M2LINK_PtrToChar> (ptr->libname), reinterpret_cast<M2LINK_PtrToChar> (libname), max (libnamelen, static_cast<unsigned int> (strlen_ (reinterpret_cast<M2LINK_PtrToChar> (ptr->libname)))))) == 0))
          {
            return ptr;
          }
        ptr = ptr->next;
      } while (! (ptr == Modules.array[state-M2Dependent_unregistered]));
    }
  return NULL;
  /* static analysis guarentees a RETURN statement will be used before here.  */
  __builtin_unreachable ();
}


/*
   LookupModule - lookup and return the ModuleChain pointer containing
                  module name from a particular list.
*/

static M2Dependent_ModuleChain LookupModule (M2Dependent_DependencyState state, void * name, void * libname)
{
  return LookupModuleN (state, name, static_cast<unsigned int> (strlen_ (reinterpret_cast<M2LINK_PtrToChar> (name))), libname, static_cast<unsigned int> (strlen_ (reinterpret_cast<M2LINK_PtrToChar> (libname))));
  /* static analysis guarentees a RETURN statement will be used before here.  */
  __builtin_unreachable ();
}


/*
   toCString - replace any character sequence 
 into a newline.
*/

static void toCString (char *str, unsigned int _str_high)
{
  unsigned int high;
  unsigned int i;
  unsigned int j;

  i = 0;
  high = _str_high;
  while (i < high)
    {
      if ((i < high) && (str[i] == '\\'))
        {
          if (str[i+1] == 'n')
            {
              str[i] = ASCII_nl;
              j = i+1;
              while (j < high)
                {
                  str[j] = str[j+1];
                  j += 1;
                }
            }
        }
      i += 1;
    }
}


/*
   strcmp - return 0 if both strings are equal.
            We cannot use Builtins.def during bootstrap.
*/

static int strcmp (M2LINK_PtrToChar a, M2LINK_PtrToChar b)
{
  if ((a != NULL) && (b != NULL))
    {
      /* avoid gcc warning by using compound statement even if not strictly necessary.  */
      if (a == b)
        {
          return 0;
        }
      else
        {
          while ((*a) == (*b))
            {
              if ((*a) == ASCII_nul)
                {
                  return 0;
                }
              a += 1;
              b += 1;
            }
        }
    }
  return 1;
  /* static analysis guarentees a RETURN statement will be used before here.  */
  __builtin_unreachable ();
}


/*
   strncmp - return 0 if both strings are equal.
             We cannot use Builtins.def during bootstrap.
*/

static int strncmp (M2LINK_PtrToChar a, M2LINK_PtrToChar b, unsigned int n)
{
  if (n == 0)
    {
      return 0;
    }
  else if ((a != NULL) && (b != NULL))
    {
      /* avoid dangling else.  */
      if (a == b)
        {
          return 0;
        }
      else
        {
          while (((*a) == (*b)) && (n > 0))
            {
              if (((*a) == ASCII_nul) || (n == 1))
                {
                  return 0;
                }
              a += 1;
              b += 1;
              n -= 1;
            }
        }
    }
  return 1;
  /* static analysis guarentees a RETURN statement will be used before here.  */
  __builtin_unreachable ();
}


/*
   strlen - returns the length of string.
*/

static int strlen_ (M2LINK_PtrToChar string)
{
  int count;

  if (string == NULL)
    {
      return 0;
    }
  else
    {
      count = 0;
      while ((*string) != ASCII_nul)
        {
          string += 1;
          count += 1;
        }
      return count;
    }
  /* static analysis guarentees a RETURN statement will be used before here.  */
  __builtin_unreachable ();
}


/*
   traceprintf - wrap printf with a boolean flag.
*/

static void traceprintf (bool flag, const char *str_, unsigned int _str_high)
{
  char str[_str_high+1];

  /* make a local copy of each unbounded array.  */
  memcpy (str, str_, _str_high+1);

  if (flag)
    {
      toCString ((char *) str, _str_high);
      libc_printf ((const char *) str, _str_high);
    }
}


/*
   traceprintf2 - wrap printf with a boolean flag.
*/

static void traceprintf2 (bool flag, const char *str_, unsigned int _str_high, void * arg)
{
  char ch;
  char str[_str_high+1];

  /* make a local copy of each unbounded array.  */
  memcpy (str, str_, _str_high+1);

  if (flag)
    {
      toCString ((char *) str, _str_high);
      if (arg == NULL)
        {
          ch = (char) 0;
          arg = &ch;
        }
      libc_printf ((const char *) str, _str_high, arg);
    }
}


/*
   traceprintf3 - wrap printf with a boolean flag.
*/

static void traceprintf3 (bool flag, const char *str_, unsigned int _str_high, void * arg1, void * arg2)
{
  char ch;
  char str[_str_high+1];

  /* make a local copy of each unbounded array.  */
  memcpy (str, str_, _str_high+1);

  if (flag)
    {
      toCString ((char *) str, _str_high);
      if (arg1 == NULL)
        {
          ch = (char) 0;
          arg1 = &ch;
        }
      if (arg2 == NULL)
        {
          ch = (char) 0;
          arg2 = &ch;
        }
      libc_printf ((const char *) str, _str_high, arg1, arg2);
    }
}


/*
   moveTo - moves mptr to the new list determined by newstate.
            It updates the mptr state appropriately.
*/

static void moveTo (M2Dependent_DependencyState newstate, M2Dependent_ModuleChain mptr)
{
  if (onChain (mptr->dependency.state, mptr))
    {
      RemoveModule (&Modules.array[mptr->dependency.state-M2Dependent_unregistered], mptr);
    }
  mptr->dependency.state = newstate;
  AppendModule (&Modules.array[mptr->dependency.state-M2Dependent_unregistered], mptr);
}


/*
   ResolveDependant -
*/

static void ResolveDependant (M2Dependent_ModuleChain mptr, void * currentmodule, void * libname)
{
  if (mptr == NULL)
    {
      traceprintf3 (DependencyTrace, (const char *) "   module %s [%s] has not been registered via a global constructor\\n", 68, currentmodule, libname);
    }
  else
    {
      if (onChain (M2Dependent_started, mptr))
        {
          traceprintf (DependencyTrace, (const char *) "   processing...\\n", 18);
        }
      else
        {
          moveTo (M2Dependent_started, mptr);
          traceprintf3 (DependencyTrace, (const char *) "   starting: %s [%s]\\n", 22, currentmodule, libname);
          (*mptr->dependency.proc.proc) ();  /* Invoke and process the dependency graph.  */
          traceprintf3 (DependencyTrace, (const char *) "   finished: %s [%s]\\n", 22, currentmodule, libname);  /* Invoke and process the dependency graph.  */
          moveTo (M2Dependent_ordered, mptr);
        }
    }
}


/*
   PerformRequestDependant - the current modulename has a dependancy upon
                             dependantmodule.  If dependantmodule is NIL then
                             modulename has no further dependants and it can be
                             resolved.
*/

static void PerformRequestDependant (void * modulename, void * libname, void * dependantmodule, void * dependantlibname)
{
  M2Dependent_ModuleChain mptr;

  traceprintf3 (DependencyTrace, (const char *) "  module %s [%s]", 16, modulename, libname);
  if (dependantmodule == NULL)
    {
      /* avoid dangling else.  */
      traceprintf (DependencyTrace, (const char *) " has finished its import graph\\n", 32);
      mptr = LookupModule (M2Dependent_unordered, modulename, libname);
      if (mptr != NULL)
        {
          traceprintf3 (DependencyTrace, (const char *) "  module %s [%s] is now ordered\\n", 33, modulename, libname);
          moveTo (M2Dependent_ordered, mptr);
        }
    }
  else
    {
      traceprintf3 (DependencyTrace, (const char *) " imports from %s [%s]\\n", 23, dependantmodule, dependantlibname);
      mptr = LookupModule (M2Dependent_ordered, dependantmodule, dependantlibname);
      if (mptr == NULL)
        {
          traceprintf3 (DependencyTrace, (const char *) "  module %s [%s] is not ordered\\n", 33, dependantmodule, dependantlibname);
          mptr = LookupModule (M2Dependent_unordered, dependantmodule, dependantlibname);
          if (mptr == NULL)
            {
              traceprintf3 (DependencyTrace, (const char *) "  module %s [%s] is not unordered\\n", 35, dependantmodule, dependantlibname);
              mptr = LookupModule (M2Dependent_started, dependantmodule, dependantlibname);
              if (mptr == NULL)
                {
                  traceprintf3 (DependencyTrace, (const char *) "  module %s [%s] has not started\\n", 34, dependantmodule, dependantlibname);
                  traceprintf3 (DependencyTrace, (const char *) "  module %s [%s] attempting to import from", 42, modulename, libname);
                  traceprintf3 (DependencyTrace, (const char *) " %s [%s] which has not registered itself via a constructor\\n", 60, dependantmodule, dependantlibname);
                }
              else
                {
                  traceprintf3 (DependencyTrace, (const char *) "  module %s [%s] has registered itself and has started\\n", 56, dependantmodule, dependantlibname);
                }
            }
          else
            {
              traceprintf3 (DependencyTrace, (const char *) "  module %s [%s] resolving\\n", 28, dependantmodule, dependantlibname);
              ResolveDependant (mptr, dependantmodule, dependantlibname);
            }
        }
      else
        {
          traceprintf3 (DependencyTrace, (const char *) "  module %s [%s]", 16, modulename, libname);
          traceprintf3 (DependencyTrace, (const char *) " dependant %s [%s] is ordered\\n", 31, dependantmodule, dependantlibname);
        }
    }
}


/*
   ResolveDependencies - resolve dependencies for currentmodule, libname.
*/

static void ResolveDependencies (void * currentmodule, void * libname)
{
  M2Dependent_ModuleChain mptr;

  mptr = LookupModule (M2Dependent_unordered, currentmodule, libname);
  while (mptr != NULL)
    {
      traceprintf3 (DependencyTrace, (const char *) "   attempting to resolve the dependants for %s [%s]\\n", 53, currentmodule, libname);
      ResolveDependant (mptr, currentmodule, libname);
      mptr = Modules.array[M2Dependent_unordered-M2Dependent_unregistered];
    }
}


/*
   DisplayModuleInfo - displays all module in the state.
*/

static void DisplayModuleInfo (M2Dependent_DependencyState state, const char *desc_, unsigned int _desc_high)
{
  M2Dependent_ModuleChain mptr;
  unsigned int count;
  char desc[_desc_high+1];

  /* make a local copy of each unbounded array.  */
  memcpy (desc, desc_, _desc_high+1);

  if (Modules.array[state-M2Dependent_unregistered] != NULL)
    {
      libc_printf ((const char *) "%s modules\\n", 12, &desc);
      mptr = Modules.array[state-M2Dependent_unregistered];
      count = 0;
      do {
        if (mptr->name == NULL)
          {
            libc_printf ((const char *) "  %d  %s []", 11, count, mptr->name);
          }
        else
          {
            libc_printf ((const char *) "  %d  %s [%s]", 13, count, mptr->name, mptr->libname);
          }
        count += 1;
        if (mptr->dependency.appl)
          {
            libc_printf ((const char *) " application", 12);
          }
        if (mptr->dependency.forc)
          {
            libc_printf ((const char *) " for C", 6);
          }
        if (mptr->dependency.forced)
          {
            libc_printf ((const char *) " forced ordering", 16);
          }
        libc_printf ((const char *) "\\n", 2);
        mptr = mptr->next;
      } while (! (mptr == Modules.array[state-M2Dependent_unregistered]));
    }
}


/*
   DumpModuleData -
*/

static void DumpModuleData (bool flag)
{
  M2Dependent_ModuleChain mptr;

  if (flag)
    {
      DisplayModuleInfo (M2Dependent_unregistered, (const char *) "unregistered", 12);
      DisplayModuleInfo (M2Dependent_unordered, (const char *) "unordered", 9);
      DisplayModuleInfo (M2Dependent_started, (const char *) "started", 7);
      DisplayModuleInfo (M2Dependent_ordered, (const char *) "ordered", 7);
    }
}


/*
   combine - dest := src + dest.  Places src at the front of list dest.
             Pre condition:  src, dest are lists.
             Post condition : dest := src + dest
                              src := NIL.
*/

static void combine (M2Dependent_DependencyState src, M2Dependent_DependencyState dest)
{
  M2Dependent_ModuleChain last;

  while (Modules.array[src-M2Dependent_unregistered] != NULL)
    {
      last = Modules.array[src-M2Dependent_unregistered]->prev;
      moveTo (M2Dependent_ordered, last);
      Modules.array[dest-M2Dependent_unregistered] = last;  /* New item is at the head.  */
    }
}


/*
   tracemodule -
*/

static void tracemodule (bool flag, void * modname, unsigned int modlen, void * libname, unsigned int liblen)
{
  typedef struct tracemodule__T4_a tracemodule__T4;

  struct tracemodule__T4_a { char array[100+1]; };
  tracemodule__T4 buffer;
  unsigned int len;

  if (flag)
    {
      len = min (modlen, sizeof (buffer)-1);
      libc_strncpy (&buffer, modname, len);
      buffer.array[len] = (char) 0;
      libc_printf ((const char *) "%s ", 3, &buffer);
      len = min (liblen, sizeof (buffer)-1);
      libc_strncpy (&buffer, libname, len);
      buffer.array[len] = (char) 0;
      libc_printf ((const char *) " [%s]", 5, &buffer);
    }
}


/*
   ForceModule -
*/

static void ForceModule (void * modname, unsigned int modlen, void * libname, unsigned int liblen)
{
  M2Dependent_ModuleChain mptr;

  traceprintf (ForceTrace, (const char *) "forcing module: ", 16);
  tracemodule (ForceTrace, modname, modlen, libname, liblen);
  traceprintf (ForceTrace, (const char *) "\\n", 2);
  mptr = LookupModuleN (M2Dependent_ordered, modname, modlen, libname, liblen);
  if (mptr != NULL)
    {
      mptr->dependency.forced = true;
      moveTo (M2Dependent_user, mptr);
    }
}


/*
   ForceDependencies - if the user has specified a forced order then we override
                       the dynamic ordering with the preference.
*/

static void ForceDependencies (void)
{
  unsigned int len;
  unsigned int modlen;
  unsigned int liblen;
  M2LINK_PtrToChar modname;
  M2LINK_PtrToChar libname;
  M2LINK_PtrToChar pc;
  M2LINK_PtrToChar start;

  if (M2LINK_ForcedModuleInitOrder != NULL)
    {
      traceprintf2 (ForceTrace, (const char *) "user forcing order: %s\\n", 24, reinterpret_cast<void *> (M2LINK_ForcedModuleInitOrder));
      pc = M2LINK_ForcedModuleInitOrder;
      start = pc;
      len = 0;
      modname = NULL;
      modlen = 0;
      libname = NULL;
      liblen = 0;
      while ((*pc) != ASCII_nul)
        {
          switch ((*pc))
            {
              case ':':
                libname = start;
                liblen = len;
                len = 0;
                pc += 1;
                start = pc;
                break;

              case ',':
                modname = start;
                modlen = len;
                ForceModule (reinterpret_cast<void *> (modname), modlen, reinterpret_cast<void *> (libname), liblen);
                libname = NULL;
                liblen = 0;
                modlen = 0;
                len = 0;
                pc += 1;
                start = pc;
                break;


              default:
                pc += 1;
                len += 1;
                break;
            }
        }
      if (start != pc)
        {
          ForceModule (reinterpret_cast<void *> (start), len, reinterpret_cast<void *> (libname), liblen);
        }
      combine (M2Dependent_user, M2Dependent_ordered);
    }
}


/*
   CheckApplication - check to see that the application is the last entry in the list.
                      This might happen if the application only imports FOR C modules.
*/

static void CheckApplication (void)
{
  M2Dependent_ModuleChain mptr;
  M2Dependent_ModuleChain appl;

  mptr = Modules.array[M2Dependent_ordered-M2Dependent_unregistered];
  if (mptr != NULL)
    {
      appl = NULL;
      do {
        if (mptr->dependency.appl)
          {
            appl = mptr;
          }
        else
          {
            mptr = mptr->next;
          }
      } while (! ((appl != NULL) || (mptr == Modules.array[M2Dependent_ordered-M2Dependent_unregistered])));
      if (appl != NULL)
        {
          RemoveModule (&Modules.array[M2Dependent_ordered-M2Dependent_unregistered], appl);
          AppendModule (&Modules.array[M2Dependent_ordered-M2Dependent_unregistered], appl);
        }
    }
}


/*
   warning3 - write format arg1 arg2 to stderr.
*/

static void warning3 (const char *format_, unsigned int _format_high, void * arg1, void * arg2)
{
  typedef struct warning3__T5_a warning3__T5;

  struct warning3__T5_a { char array[4096+1]; };
  warning3__T5 buffer;
  int len;
  char format[_format_high+1];

  /* make a local copy of each unbounded array.  */
  memcpy (format, format_, _format_high+1);

  if (WarningTrace)
    {
      len = libc_snprintf (&buffer, static_cast<size_t> (sizeof (buffer)), (const char *) "warning: ", 9);
      libc_write (2, &buffer, static_cast<size_t> (len));
      len = libc_snprintf (&buffer, static_cast<size_t> (sizeof (buffer)), (const char *) format, _format_high, arg1, arg2);
      libc_write (2, &buffer, static_cast<size_t> (len));
    }
}


/*
   equal - return TRUE if C string cstr is equal to str.
*/

static bool equal (void * cstr, const char *str_, unsigned int _str_high)
{
  char str[_str_high+1];

  /* make a local copy of each unbounded array.  */
  memcpy (str, str_, _str_high+1);

  return (strncmp (reinterpret_cast<M2LINK_PtrToChar> (cstr), reinterpret_cast<M2LINK_PtrToChar> (&str), StrLib_StrLen ((const char *) str, _str_high))) == 0;
  /* static analysis guarentees a RETURN statement will be used before here.  */
  __builtin_unreachable ();
}


/*
   SetupDebugFlags - By default assigns ModuleTrace, DependencyTrace,
                     DumpPostInit to FALSE.  It checks the environment
                     GCC_M2LINK_RTFLAG which can contain
                     "all,module,hex,pre,post,dep,force".  all turns them all on.
                     The flag meanings are as follows and flags the are in
                     execution order.

                     module   generate trace info as the modules are registered.
                     hex      dump the modules ctor functions address in hex.
                     pre      generate a list of all modules seen prior to having
                              their dependancies resolved.
                     dep      display a trace as the modules are resolved.
                     post     generate a list of all modules seen after having
                              their dependancies resolved dynamically.
                     force    generate a list of all modules seen after having
                              their dependancies resolved and forced.
*/

static void SetupDebugFlags (void)
{
  typedef char *SetupDebugFlags__T1;

  SetupDebugFlags__T1 pc;

  ModuleTrace = false;
  DependencyTrace = false;
  PostTrace = false;
  PreTrace = false;
  ForceTrace = false;
  HexTrace = false;
  WarningTrace = false;
  pc = static_cast<SetupDebugFlags__T1> (libc_getenv (const_cast<void*> (reinterpret_cast<const void*>("GCC_M2LINK_RTFLAG"))));
  while ((pc != NULL) && ((*pc) != ASCII_nul))
    {
      if (equal (reinterpret_cast<void *> (pc), (const char *) "all", 3))
        {
          ModuleTrace = true;
          DependencyTrace = true;
          PreTrace = true;
          PostTrace = true;
          ForceTrace = true;
          HexTrace = true;
          WarningTrace = true;
          pc += 3;
        }
      else if (equal (reinterpret_cast<void *> (pc), (const char *) "module", 6))
        {
          /* avoid dangling else.  */
          ModuleTrace = true;
          pc += 6;
        }
      else if (equal (reinterpret_cast<void *> (pc), (const char *) "warning", 7))
        {
          /* avoid dangling else.  */
          WarningTrace = true;
          pc += 7;
        }
      else if (equal (reinterpret_cast<void *> (pc), (const char *) "hex", 3))
        {
          /* avoid dangling else.  */
          HexTrace = true;
          pc += 3;
        }
      else if (equal (reinterpret_cast<void *> (pc), (const char *) "dep", 3))
        {
          /* avoid dangling else.  */
          DependencyTrace = true;
          pc += 3;
        }
      else if (equal (reinterpret_cast<void *> (pc), (const char *) "pre", 3))
        {
          /* avoid dangling else.  */
          PreTrace = true;
          pc += 3;
        }
      else if (equal (reinterpret_cast<void *> (pc), (const char *) "post", 4))
        {
          /* avoid dangling else.  */
          PostTrace = true;
          pc += 4;
        }
      else if (equal (reinterpret_cast<void *> (pc), (const char *) "force", 5))
        {
          /* avoid dangling else.  */
          ForceTrace = true;
          pc += 5;
        }
      else
        {
          /* avoid dangling else.  */
          pc += 1;
        }
    }
}


/*
   Init - initialize the debug flags and set all lists to NIL.
*/

static void Init (void)
{
  M2Dependent_DependencyState state;

  SetupDebugFlags ();
  for (state=M2Dependent_unregistered; state<=M2Dependent_user; state= static_cast<M2Dependent_DependencyState>(static_cast<int>(state+1)))
    {
      Modules.array[state-M2Dependent_unregistered] = NULL;
    }
}


/*
   CheckInitialized - checks to see if this module has been initialized
                      and if it has not it calls Init.  We need this
                      approach as this module is called by module ctors
                      before we reach main.
*/

static void CheckInitialized (void)
{
  if (! Initialized)
    {
      Initialized = true;
      Init ();
    }
}


/*
   ConstructModules - resolve dependencies and then call each
                      module constructor in turn.
*/

extern "C" void M2Dependent_ConstructModules (void * applicationmodule, void * libname, int argc, void * argv, void * envp)
{
  M2Dependent_ModuleChain mptr;
  M2Dependent_ArgCVEnvP nulp;

  CheckInitialized ();
  traceprintf3 (ModuleTrace, (const char *) "application module: %s [%s]\\n", 29, applicationmodule, libname);
  mptr = LookupModule (M2Dependent_unordered, applicationmodule, libname);
  if (mptr != NULL)
    {
      mptr->dependency.appl = true;
    }
  traceprintf (PreTrace, (const char *) "Pre resolving dependents\\n", 26);
  DumpModuleData (PreTrace);
  ResolveDependencies (applicationmodule, libname);
  traceprintf (PreTrace, (const char *) "Post resolving dependents\\n", 27);
  DumpModuleData (PostTrace);
  ForceDependencies ();
  traceprintf (ForceTrace, (const char *) "After user forcing ordering\\n", 29);
  DumpModuleData (ForceTrace);
  CheckApplication ();
  traceprintf (ForceTrace, (const char *) "After runtime forces application to the end\\n", 45);
  DumpModuleData (ForceTrace);
  if (Modules.array[M2Dependent_ordered-M2Dependent_unregistered] == NULL)
    {
      traceprintf3 (ModuleTrace, (const char *) "  module: %s [%s] has not registered itself using a global constructor\\n", 72, applicationmodule, libname);
      traceprintf2 (ModuleTrace, (const char *) "  hint try compile and linking using: gm2 %s.mod\\n", 50, applicationmodule);
      traceprintf2 (ModuleTrace, (const char *) "  or try using: gm2 -fscaffold-static %s.mod\\n", 46, applicationmodule);
    }
  else
    {
      mptr = Modules.array[M2Dependent_ordered-M2Dependent_unregistered];
      do {
        if (mptr->dependency.forc)
          {
            traceprintf3 (ModuleTrace, (const char *) "initializing module: %s [%s] for C\\n", 36, mptr->name, mptr->libname);
          }
        else
          {
            traceprintf3 (ModuleTrace, (const char *) "initializing module: %s [%s]\\n", 30, mptr->name, mptr->libname);
          }
        if (mptr->dependency.appl)
          {
            traceprintf3 (ModuleTrace, (const char *) "application module: %s [%s]\\n", 29, mptr->name, mptr->libname);
            traceprintf (ModuleTrace, (const char *) "  calling M2RTS_ExecuteInitialProcedures\\n", 42);
            M2RTS_ExecuteInitialProcedures ();
            traceprintf (ModuleTrace, (const char *) "  calling application module\\n", 30);
          }
        (*mptr->init.proc) (argc, argv, envp);
        mptr = mptr->next;
      } while (! (mptr == Modules.array[M2Dependent_ordered-M2Dependent_unregistered]));
    }
}


/*
   DeconstructModules - resolve dependencies and then call each
                        module constructor in turn.
*/

extern "C" void M2Dependent_DeconstructModules (void * applicationmodule, void * libname, int argc, void * argv, void * envp)
{
  M2Dependent_ModuleChain mptr;

  traceprintf3 (ModuleTrace, (const char *) "application module finishing: %s [%s]\\n", 39, applicationmodule, libname);
  if (Modules.array[M2Dependent_ordered-M2Dependent_unregistered] == NULL)
    {
      traceprintf (ModuleTrace, (const char *) "  no ordered modules found during finishing\\n", 45);
    }
  else
    {
      traceprintf (ModuleTrace, (const char *) "ExecuteTerminationProcedures\\n", 30);
      M2RTS_ExecuteTerminationProcedures ();
      traceprintf (ModuleTrace, (const char *) "terminating modules in sequence\\n", 33);
      mptr = Modules.array[M2Dependent_ordered-M2Dependent_unregistered]->prev;
      do {
        if (mptr->dependency.forc)
          {
            traceprintf3 (ModuleTrace, (const char *) "finalizing module: %s [%s] for C\\n", 34, mptr->name, mptr->libname);
          }
        else
          {
            traceprintf3 (ModuleTrace, (const char *) "finalizing module: %s [%s]\\n", 28, mptr->name, mptr->libname);
          }
        (*mptr->fini.proc) (argc, argv, envp);
        mptr = mptr->prev;
      } while (! (mptr == Modules.array[M2Dependent_ordered-M2Dependent_unregistered]->prev));
    }
}


/*
   RegisterModule - adds module name to the list of outstanding
                    modules which need to have their dependencies
                    explored to determine initialization order.
*/

extern "C" void M2Dependent_RegisterModule (void * modulename, void * libname, M2Dependent_ArgCVEnvP init, M2Dependent_ArgCVEnvP fini, PROC dependencies)
{
  M2Dependent_ModuleChain mptr;

  CheckInitialized ();
  if (! M2LINK_StaticInitialization)
    {
      mptr = LookupModule (M2Dependent_unordered, modulename, libname);
      if (mptr == NULL)
        {
          traceprintf3 (ModuleTrace, (const char *) "module: %s [%s] registering", 27, modulename, libname);
          moveTo (M2Dependent_unordered, CreateModule (modulename, libname, init, fini, dependencies));
          traceprintf (ModuleTrace, (const char *) "\\n", 2);
        }
      else
        {
          warning3 ((const char *) "module: %s [%s] (ignoring duplicate registration)\\n", 51, modulename, libname);
        }
    }
}


/*
   RequestDependant - used to specify that modulename is dependant upon
                      module dependantmodule.  It only takes effect
                      if we are not using StaticInitialization.
*/

extern "C" void M2Dependent_RequestDependant (void * modulename, void * libname, void * dependantmodule, void * dependantlibname)
{
  CheckInitialized ();
  if (! M2LINK_StaticInitialization)
    {
      PerformRequestDependant (modulename, libname, dependantmodule, dependantlibname);
    }
}

extern "C" void _M2_M2Dependent_init (__attribute__((unused)) int argc,__attribute__((unused)) char *argv[],__attribute__((unused)) char *envp[])
{
  CheckInitialized ();
}

extern "C" void _M2_M2Dependent_fini (__attribute__((unused)) int argc,__attribute__((unused)) char *argv[],__attribute__((unused)) char *envp[])
{
}
